import tkinter as tk
import random
from tkinter import ttk
import akshare as ak
import threading
import time
import requests
import re
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from fake_useragent import UserAgent


class StockMonitor:
    def __init__(self, root):
        self.root = root
        root.title("A股实时监控 v3.0")
        root.configure(bg='#FF0000')
        root.geometry("1400x50")

        # 初始化界面
        self.setup_ui()

        # 数据缓存
        self.cache = {'last_update': None}

        # 启动更新线程
        self.running = True
        threading.Thread(target=self.update_loop, daemon=True).start()

    def setup_ui(self):
        """创建带滚动条的界面"""
        self.container = tk.Canvas(self.root, bg='#FF0000', highlightthickness=0)
        self.scrollbar = ttk.Scrollbar(self.root, orient="horizontal", command=self.container.xview)
        self.frame = tk.Frame(self.container, bg='#FF0000')

        # 配置滚动区域
        self.container.configure(xscrollcommand=self.scrollbar.set)
        self.container.pack(side="top", fill="x", expand=True)
        self.scrollbar.pack(side="bottom", fill="x")
        self.container.create_window((0, 0), window=self.frame, anchor="nw")

        # 创建数据标签
        self.labels = {}
        columns = [
            '更新时间', '上证指数', '深证成指', '创业板指', '北证50',
            '上涨家数', '下跌家数', '成交量', '涨停家数',
            '跌停家数', '封板率', 'A50期指', '昨首板', '昨连板'
        ]

        for col in columns:
            frame = tk.Frame(self.frame, bg='#FF0000', padx=8)
            label = tk.Label(frame, text=f"{col}: --", font=('微软雅黑', 12),
                             fg='white', bg='#FF0000')
            label.pack()
            frame.pack(side='left', fill='x')
            self.labels[col] = label

    def update_loop(self):
        """数据更新主循环"""
        while self.running:
            try:
                data = self.get_combined_data()
                self.root.after(0, self.update_ui, data)
            except Exception as e:
                print(f"更新失败: {str(e)}")
            time.sleep(15)  # 更新间隔调整为15秒

    def get_combined_data(self):
        """整合多源数据"""
        data = {}

        try:
            # 主数据源：同花顺
            web_data = self.get_ths_data()
            data.update(web_data)
        except Exception as e:
            print(f"主数据源获取失败: {str(e)}")
            data.update(self.get_fallback_data())

        # # 补充数据
        # data.update({
        #     'A50期指': self.get_a50_data(),
        #     '更新时间': time.strftime("%H:%M:%S"),
        #     **self.get_yesterday_data()
        # })

        return data

    def get_ths_data(self):
        driver = None
        try:
            options = Options()
            options.add_argument("--headless")
            options.add_argument("--disable-blink-features=AutomationControlled")
            options.add_experimental_option("excludeSwitches", ["enable-automation"])
            options.add_argument(f"user-agent={UserAgent().random}")
            options.add_argument("--window-size=1920,1080")
            driver = webdriver.Chrome(options=options)
            print("正在访问同花顺...")
            driver.get("http://q.10jqka.com.cn/")

            # 先访问一次首页建立cookie
            # driver.get("https://www.10jqka.com.cn")
            time.sleep(random.uniform(1, 3))
            # 注入JS代码隐藏WebDriver特征
            driver.execute_script(
                "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
            )
            # 模拟人类操作
            driver.execute_script("window.scrollBy(0, 500)")
            time.sleep(random.uniform(0.5, 1.5))

            WebDriverWait(driver, 20).until(
                EC.presence_of_element_located((By.CLASS_NAME, "hcharts-list")))

            # 保存页面源码用于调试
            with open("debug_page.html", "w", encoding="utf-8") as f:
                f.write(driver.page_source)
            print("页面已保存至 debug_page.html")

            # WebDriverWait(driver, 15).until(
            #     EC.presence_of_element_located((By.CLASS_NAME, "hcharts-list"))
            # )
            soup = BeautifulSoup(driver.page_source, 'lxml', from_encoding="utf-8")
            print(soup.select(".c-rise")[0].text)
            return self.parse_ths_page(soup)
        except Exception as e:
            print(f"Selenium 获取数据失败: {str(e)}")
            raise
        finally:
            if driver:
                driver.quit()

    def parse_ths_page(self, soup):
        """解析同花顺页面数据"""

        def safe_extract(element, selector, attr="text", pattern=r"\d+"):
            """安全数据提取函数"""
            try:
                target = element.select_one(selector)
                if not target: return 0
                content = target.get(attr) if attr != "text" else target.text
                match = re.search(pattern, content.replace(",", ""))
                return int(match.group()) if match else 0
            except:
                return 0

        # 获取基础容器
        market_container = soup.select_one(".hcharts-list")

        # 解析市场数据
        market_data = {
            # '上涨家数': safe_extract(market_container, ".cur .c-rise"),
            # '下跌家数': safe_extract(market_container, ".cur .c-fall"),
            # '涨停家数': safe_extract(market_container, ".item:nth-of-type(2) .c-rise"),
            # '跌停家数': safe_extract(market_container, ".item:nth-of-type(2) .c-fall")
            '上涨家数':market_container.select_one(".cur .c-rise"),
            '下跌家数':market_container.select_one(".cur .c-fall"),
            '涨停家数':market_container.select_one(".item:nth-of-type(2) .c-rise"),
            '跌停家数':market_container.select_one(".item:nth-of-type(2) .c-fall")
        }

        # 解析指数数据
        index_data = {}
        for index_div in soup.select(".flash-single"):
            code = index_div.select_one(".price-box")["data-code"].split("_")[-1]
            name_map = {
                "1A0001": "上证指数",
                "399001": "深证成指",
                "899050": "北证50",
                "399006": "创业板指"
            }
            index_name = name_map.get(code, code)

            index_data[index_name] = {
                'price': safe_extract(index_div, ".data-line span:nth-of-type(2)", pattern=r"\d+\.?\d*"),
                'volume': safe_extract(index_div, ".data-line span:nth-of-type(3)", pattern=r"[\d.]+")
            }

        # 计算衍生数据
        market_data.update({
            '上证指数': index_data.get("上证指数", {}).get("price", 0),
            '深证成指': index_data.get("深证成指", {}).get("price", 0),
            '创业板指': index_data.get("创业板指", {}).get("price", 0),
            '北证50': index_data.get("北证50", {}).get("price", 0),
            '成交量': f"{sum([v.get('volume', 0) for v in index_data.values()]) / 10000:.1f}亿",
            '封板率': f"{market_data['涨停家数'] / (market_data['涨停家数'] + market_data['跌停家数']) * 100:.1f}%"
            if (market_data['涨停家数'] + market_data['跌停家数']) > 0 else "0.0%"
        })

        return market_data

    def get_fallback_data(self):
        """备用数据源（akshare）"""
        try:
            index_df = ak.stock_zh_index_spot_em()
            return {
                '上证指数': index_df[index_df['代码'] == 'sh000001']['最新价'].values[0],
                '创业板指': index_df[index_df['代码'] == 'sz399006']['最新价'].values[0]
            }
        except:
            return {'上证指数': '--', '创业板指': '--'}

    def get_a50_data(self):
        """获取A50期指数据"""
        try:
            futures_df = ak.futures_zh_spot(subscribe_list=['CN_A50'])
            return futures_df.iloc[0]['最新价']
        except:
            return self.crawl_a50_fallback()

    def crawl_a50_fallback(self):
        """A50备用爬虫方案"""
        try:
            url = "https://cn.investing.com/indices/ftse-china-a50"
            headers = {'User-Agent': UserAgent().random}
            response = requests.get(url, headers=headers, timeout=10)
            soup = BeautifulSoup(response.text, 'lxml')
            return soup.find('span', {'data-test': 'instrument-price-last'}).text
        except:
            return '--'

    # def get_yesterday_data(self):
    #     """获取昨日连板数据"""
    #     try:
    #         url = "https://data.10jqka.com.cn/market/zygcfx/"
    #         headers = {'User-Agent': UserAgent().random}
    #         response = requests.get(url, headers=headers, timeout=10)
    #         soup = BeautifulSoup(response.text, 'lxml')
    #
    #         return {
    #             '昨首板': soup.find('td', text='首板').find_next('td').text.strip(),
    #             '昨连板': soup.find('td', text='连板').find_next('td').text.strip()
    #         }
    #     except:
    #         return {'昨首板': '--', '昨连板': '--'}

    def update_ui(self, data):
        """更新界面显示"""
        try:
            # 基础指数数据
            self.labels['上证指数'].config(text=f"上证指数: {data.get('上证指数', '--')}")
            self.labels['深证成指'].config(text=f"深证成指: {data.get('深证成指', '--')}")
            self.labels['创业板指'].config(text=f"创业板指: {data.get('创业板指', '--')}")
            self.labels['北证50'].config(text=f"北证50: {data.get('北证50', '--')}")

            # 市场行情数据
            self.labels['上涨家数'].config(text=f"上涨家数: {data.get('上涨家数', '--')}")
            self.labels['下跌家数'].config(text=f"下跌家数: {data.get('下跌家数', '--')}")
            self.labels['成交量'].config(text=f"成交量: {data.get('成交量', '--')}")

            # 特殊指标
            self.labels['涨停家数'].config(text=f"涨停家数: {data.get('涨停家数', '--')}")
            self.labels['跌停家数'].config(text=f"跌停家数: {data.get('跌停家数', '--')}")
            self.labels['封板率'].config(text=f"封板率: {data.get('封板率', '--')}")
            self.labels['A50期指'].config(text=f"A50期指: {data.get('A50期指', '--')}")

            # 昨日数据
            self.labels['昨首板'].config(text=f"昨首板: {data.get('昨首板', '--')}")
            self.labels['昨连板'].config(text=f"昨连板: {data.get('昨连板', '--')}")

            # 更新时间
            self.labels['更新时间'].config(text=f"更新时间: {data.get('更新时间', '--')}")

            # 更新滚动区域
            self.frame.update_idletasks()
            self.container.config(scrollregion=self.container.bbox("all"))
        except Exception as e:
            print(f"界面更新异常: {str(e)}")

    def on_closing(self):
        """关闭窗口处理"""
        self.running = False
        self.root.destroy()


if __name__ == "__main__":
    root = tk.Tk()
    app = StockMonitor(root)
    root.protocol("WM_DELETE_WINDOW", app.on_closing)
    root.mainloop()